/*
	NounField.cpp
	(c)2004 Palestar Inc, Richard Lyle
*/


#include "Constants.h"
#include "NounField.h"
#include "GameContext.h"

//----------------------------------------------------------------------------

const dword FIELD_UPDATE_INTERVAL = TICKS_PER_SECOND * 2.5;

//----------------------------------------------------------------------------

IMPLEMENT_ABSTRACT_FACTORY( NounField, NounGame ); 
REGISTER_FACTORY_KEY( NounField, 4635871350937866294 );

BEGIN_ABSTRACT_PROPERTY_LIST( NounField, NounGame );
	ADD_PROPERTY( m_pLink );
	ADD_PROPERTY( m_fFieldRadius );
	ADD_PROPERTY( m_InField );
END_PROPERTY_LIST();

NounField::NounField() : m_fFieldRadius( 0 ), m_nUpdateField( 0 )
{}

//----------------------------------------------------------------------------

const int VERSION_20040315 = 20040315;

bool NounField::read( InStream & input )
{
	if (! Noun::read( input ) )
		return false;
	m_nUpdateField = (key() + tick()) % FIELD_UPDATE_INTERVAL;
	return true;
}

//----------------------------------------------------------------------------

NounType NounField::nounType() const
{
	return TYPE_EFFECT;
}

bool NounField::collidable() const
{
	return true;
}

bool NounField::canBeDetected() const
{
	return false;
}

//----------------------------------------------------------------------------

void NounField::initialize()
{
	Noun::initialize();

	if ( validateNoun( m_pLink ) )
	{
		if ( m_pLink->zone() != zone() )
			setWorldPosition( m_pLink->worldPosition() );		// use setWorldPosition() only when the zones have changed, otherwise use setPosition() which is more accurate
		else
			setPosition( m_pLink->zonePosition() );

		for(int i=m_InField.size()-1;i>=0;i--)
		{
			Noun * pNoun = m_InField[ i ];
			if ( pNoun != NULL )
				onEnterField( pNoun );
			else
				m_InField.remove( i );
		}
	}
}

void NounField::release()
{
	Noun::release();

	for(int i=0;i<m_InField.size();i++)
	{
		Noun * pNoun = m_InField[ i ];
		if ( pNoun != NULL )
			onLeaveField( pNoun );
	}
	m_InField.release();
}

void NounField::simulate( dword nTick )
{
	if ( validateNoun( m_pLink ) )
	{
		// move to the same position of our linked object
		if ( m_pLink->zone() != zone() )
			setWorldPosition( m_pLink->worldPosition() );		// use setWorldPosition() only when the zones have changed, otherwise use setPosition() which is more accurate
		else
			setPosition( m_pLink->zonePosition() );

		if ( nTick >= m_nUpdateField )
		{
			m_nUpdateField = nTick + FIELD_UPDATE_INTERVAL;
			for(int i=m_InField.size()-1;i>=0;i--)
			{
				Noun * pNoun = m_InField[ i ];
				if (! validateNoun( pNoun ) || (pNoun->worldPosition() - worldPosition()).magnitude() > m_fFieldRadius )
				{
					if ( pNoun != NULL )
						onLeaveField( pNoun );

					m_InField.remove( i );
				}
			}
		}
	}
	else
	{
		// our link is gone, detach this field object
		setDetach();
	}

	Noun::simulate( nTick );
}

void NounField::collision( Noun * pCollide, const Vector3 & intersect )
{
	if ( validateNoun( m_pLink ) )
	{
		if ( ! m_InField.find( pCollide->key() ).valid() )
		{
			m_InField.insert( pCollide->key(), pCollide );
			onEnterField( pCollide );
		}

		onInsideField( pCollide );
	}
}

//----------------------------------------------------------------------------

void NounField::setLink( Noun * pNoun )
{
	// set the link
	m_pLink = pNoun;
}

void NounField::setFieldRadius( float fRadius )
{
	m_fFieldRadius = fRadius;
}

//----------------------------------------------------------------------------
//EOF
